package singleflight

import "sync"

// 正在进行或者已经结束的请求

type call struct {
	wg sync.WaitGroup
	val interface{}
	err error
}
// 管理不同key对应的请求。
type Group struct {

	mu sync.Mutex
	m map[string]*call
}


func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
	g.mu.Lock()
	// 延迟加载
	if g.m == nil {
		g.m = make(map[string]*call)
	}

	// 检查此key对应的查询是否正在进行中？
	if c, ok := g.m[key]; ok {
		// 等待解锁，返回值
		g.mu.Unlock()
		c.wg.Wait() // 堵塞，直到锁被释放
		return c.val, c.err

	}
	// 如果没有对应的请求，则需要创建新的
	c := new(call)
	// 开始发起请求
	c.wg.Add(1)
	// 存储起来
	g.m[key] = c
	// 解锁
	g.mu.Unlock()
	// 发起请求，获得值
	c.val, c.err = fn()
	// 等待所有的 goroutine 返回
	c.wg.Done()
	// 操作 map 需要上锁
	g.mu.Lock()
	delete(g.m, key)
	// 删除对应 call 后 解锁
	g.mu.Unlock()

	return c.val, c.err
}

